package state;

import state.bean.GameRect;
import state.component.Component;
import state.util.GameBoolean;

/**
 * Class dùng để lưu thông tin xử lý về các đối tượng Image và tự động cập nhật màn hình thông qua manager
 * 
 * @author ptran2
 * @version 0.1
 */
public class Sprite extends GameRect {
  public static final int TRANS_NONE = 0;

  public static final int TRANS_ROT90 = 5;

  public static final int TRANS_ROT180 = 3;

  public static final int TRANS_ROT270 = 6;

  public static final int TRANS_MIRROR = 2;

  public static final int TRANS_MIRROR_ROT90 = 7;

  public static final int TRANS_MIRROR_ROT180 = 1;

  public static final int TRANS_MIRROR_ROT270 = 4;

  public Sprite previous = null;
  public Sprite next = null;
  public Manager _manager = null;
  public GameImage _image = null;

  public KeySpriteListener _keyListener = null;
  public MouseListener _mouseListener = null;
  public MouseMoveListener _mouseMoveListener = null;
  public TimerListener _timerListener = null;
  public TouchScreenListener _touchScreenListener = null;
  public DrawListener _drawListener = null;

  public byte _id = 0;
  public boolean _visible = true;
  public int _anchor = GameConstants.TOP_LEFT_ANCHOR;

  public int _realX = 0;
  public int _realY = 0;

  public Sprite(GameImage image, Manager manager, int x, int y, int anchor) {
    super(x, y, GameConstants.SCREEN_WIDTH, GameConstants.SCREEN_HEIGHT);
    if (manager != null) {
      manager.append(this);
    }
    this._x = x;
    this._y = y;
    if (image != null) {
      this._width = image.getWidth();
      this._height = image.getHeight();
    }
    this._realX = calculateX(x, anchor, _width);
    this._realY = calculateY(y, anchor, _height);
    this._anchor = anchor;
    setImage(image);
  }

  public byte getId() {
    return _id;
  }

  public Sprite setId(byte id) {
    this._id = id;
    return this;
  }

  public Sprite setAnchor(int anchor) {
    this._anchor = anchor;
    this._realX = calculateX(_x, anchor, _width);
    this._realY = calculateY(_y, anchor, _height);
    return this;
  }

  public int getAnchor() {
    return _anchor;
  }

  /**
   * Xác định class sẽ handle xử lý sự kiện phím
   * 
   * @param keyListener
   *          - Class handle xử lý phím
   */
  public Sprite setKeyListener(KeySpriteListener keyListener) {
    this._keyListener = keyListener;
    return this;
  }

  /**
   * Xác định class sẽ handle xử lý sự kiện chuột
   * 
   * @param mouseListener
   *          - Class handle xử lý chuột
   */
  public Sprite setMouseListener(MouseListener mouseListener) {
    this._mouseListener = mouseListener;
    return this;
  }

  public Sprite setMouseMoveListener(MouseMoveListener mouseMoveListener) {
    if (_manager == null) {
      System.out.println("Sprite : setMouseMoveListener : manager is null");
      return this;
    }
    
    this._mouseMoveListener = mouseMoveListener;
    _manager.useMouseMoveEvent();
    return this;
  }

  public Sprite setTouchScreenListener(TouchScreenListener touchScreenListener) {
    this._touchScreenListener = touchScreenListener;
    return this;
  }

  /**
   * Xác định class sẽ handle xử lý vẽ cho Sprite này
   * 
   * @param drawListener
   *          - Class handle xử lý vẽ
   */
  public Sprite setDrawListener(DrawListener drawListener) {
    this._drawListener = drawListener;
    return this;
  }

  public Sprite setTimerListener(TimerListener timerListener) {
    if (_manager == null) {
      System.out.println("Sprite : setTimerListener : manager is null");
      return this;
    }
    
    if (this._timerListener != null) {
      GameGlobal.systemCanvas.getTimer().removeTarget(this._timerListener);
      this._timerListener = null;
    }
    GameGlobal.systemCanvas.getTimer().addTarget(timerListener);
    this._timerListener = timerListener;
    return this;
  }

  public Sprite removeTimerListener() {
    if (_manager == null) {
      return this;
    }

    if (_timerListener != null) {
      GameGlobal.systemCanvas.getTimer().removeTarget(_timerListener);
      _timerListener = null;
    }
    return this;
  }

  public int getHeight() {
    return _height;
  }

  public int getWidth() {
    return _width;
  }

  public Component setFocused(boolean isFocused) {
    return null;
  }

  public Sprite setWidth(int width) {
    this._width = width;
    this._realX = calculateX(_x, _anchor, width);
    return this;
  }

  public Sprite setHeight(int height) {
    this._height = height;
    this._realY = calculateY(_y, _anchor, height);
    return this;
  }

  public Sprite setSize(int width, int height) {
    if (this._width != width) {
      setWidth(width);
    }
    if (this._height != height) {
      setHeight(height);
    }
    return this;
  }

  public int getRealX() {
    return _realX;
  }

  public int getRealY() {
    return _realY;
  }

  public int getX() {
    return _x;
  }

  public int getY() {
    return _y;
  }

  public Manager getManager() {
    return _manager;
  }

  public static int calculateX(int value, int anchor1, int width1) {
    switch (anchor1) {
    case GameConstants.TOP_LEFT_ANCHOR:
    case GameConstants.VCENTER_LEFT_ANCHOR:
    case GameConstants.BOTTOM_LEFT_ANCHOR:
      return value;
    case GameConstants.TOP_HCENTER_ANCHOR:
    case GameConstants.CENTER_ANCHOR:
    case GameConstants.BOTTOM_HCENTER_ANCHOR:
      return value - width1 / 2;
    case GameConstants.TOP_RIGHT_ANCHOR:
    case GameConstants.VCENTER_RIGHT_ANCHOR:
    case GameConstants.BOTTOM_RIGHT_ANCHOR:
      return value - width1;
    default:
      return value;
    }
  }

  public static int calculateY(int value, int anchor1, int height1) {
    switch (anchor1) {
    case GameConstants.TOP_LEFT_ANCHOR:
    case GameConstants.TOP_HCENTER_ANCHOR:
    case GameConstants.TOP_RIGHT_ANCHOR:
      return value;
    case GameConstants.VCENTER_LEFT_ANCHOR:
    case GameConstants.CENTER_ANCHOR:
    case GameConstants.VCENTER_RIGHT_ANCHOR:
      return value - height1 / 2;
    case GameConstants.BOTTOM_LEFT_ANCHOR:
    case GameConstants.BOTTOM_HCENTER_ANCHOR:
    case GameConstants.BOTTOM_RIGHT_ANCHOR:
      return value - height1;
    default:
      return value;
    }
  }

  public boolean isVisible() {
    return _visible;
  }

  public Sprite setVisible(boolean isVisible) {
    if (this._visible != isVisible) {
      this._visible = isVisible;
    }
    return this;
  }

  /**
   * Cập nhật tọa độ cho Sprite, vị trí mới của Sprite sẽ được tự động cập nhật vào Graphic
   * 
   * @param x
   *          - Tọa độ x
   * @param y
   *          - Tọa độ y
   */
  public Sprite setPosition(int x, int y) {
    if ((this._x == x) && (this._y == y)) {
      return this;
    }

    this._x = x;
    this._y = y;
    this._realX = calculateX(x, _anchor, _width);
    this._realY = calculateY(y, _anchor, _height);
    return this;
  }

  /**
   * Di chuyển Sprite 1 khoảng dx, dy , vị trí mới của Sprite sẽ được tự động cập nhật vào Graphic
   * 
   * @param dx
   *          - Khoảng di chuyển x
   * @param dy
   *          - Khoảng di chuyển y
   */
  public Sprite move(int dx, int dy) {
    if ((dx == 0) && (dy == 0)) {
      return this;
    }

    _x += dx;
    _y += dy;
    this._realX = calculateX(_x, _anchor, _width);
    this._realY = calculateY(_y, _anchor, _height);
    return this;
  }

  /**
   * Thay đổi ảnh của Sprite
   * 
   * @param img
   *          - Ảnh mới cập nhật cho Sprite
   * @param frameWidth
   *          - Chiều rộng của frame ảnh trong img
   * @param frameHeight
   *          - Chiều dài của frame ảnh trong img
   */
  public Sprite setImage(GameImage image) {
    setImage1(image, true);
    return this;
  }

  public Sprite setImage1(GameImage image, boolean changeSpriteSizeByImage) {
    if (image == this._image) {
      return this;
    }

    if (image == null) {
      this._image = null;
      return this;
    }

    this._image = image;
    if (image.getImage() == null) {
      LoadResourceCompleteListener listener = new LoadResourceCompleteListener(image.getName()) {
        public void onLoadResourceDone(GameImage image1) {
          if (((GameBoolean) get("changeSpriteSizeByImage")).booleanValue()) {
            setSize(image1.getWidth(), image1.getHeight());
          }
        }
      }.put("changeSpriteSizeByImage", new GameBoolean(changeSpriteSizeByImage));
      image.addUpdateImageListener(listener);
      return this;
    }

    if (changeSpriteSizeByImage) {
      setSize(image.getWidth(), image.getHeight());
    }
    return this;
  }

  public GameImage getImage() {
    return _image;
  }

  /**
   * Wrapper lại hàm paint(g) của Sprite, sử dụng cho class CustomSprite implement lại
   * 
   * @param g
   *          - Graphic
   */
  public Sprite paint(Graphics g) {
    if (!_visible) {
      return this;
    }

    if (_image != null) {
      g.drawImage(_image, _x, _y, _anchor);
    }
    if (_drawListener != null) {
      _drawListener.paint(this, g);
    }
    return this;
  }

  /**
   * Kiểm tra va chạm với 1 sprite khác bằng cách kiểm tra đường bao
   * 
   * @param linkedSprite
   *          - the Image to test for collision
   */
  public boolean checkCollides1(Sprite linkedSprite) {
    if (!linkedSprite._visible) {
      return false;
    }

    if (linkedSprite == this) {
      return true;
    }

    return checkCollidesWithRect(linkedSprite.getRealX(), linkedSprite.getRealY(), linkedSprite.getWidth(), linkedSprite.getHeight());
  }

  /**
   * Kiểm tra va chạm với 1 điểm bằng cách kiểm tra xem điểm tương ứng trên Sprite có trong suốt hay không, nếu là điểm không
   * trong suốt thì được coi là va chạm
   * 
   * @param inputX
   *          - Tọa độ x của điểm cần kiểm tra
   * @param inputY
   *          - Tọa độ y của điểm cần kiểm tra
   */
  public boolean checkCollides(int inputX, int inputY) {
    if (!_visible) {
      return false;
    }

    // Chuyển từ tọa độ màn hình (x, y) về tọa độ của ảnh (x1, y1)
    int x1 = inputX - getRealX();
    int y1 = inputY - getRealY();

    // Nếu tọa độ nằm ngoài ảnh thì coi như không va chạm
    if ((0 > x1) || (x1 > _width) || (0 > y1) || (y1 > _height)) {
      return false;
    }

    if (_image == null || _image.getImage() == null) {
      return true;
    }

    // Kiểm tra xem điểm ảnh cần xét có trong suốt không
    int value = _image.getImage().getPixel(inputX, inputY);
    if ((value == 0x00000000) || ((value & 0xFF000000) == 0x00000000) || (value == 0xFFFFFFFF)) {
      return false;
    }
    return true;
  }

  /**
   * Kiểm tra va chạm với 1 khoảng chữ nhật bằng cách kiểm tra đường bao
   * 
   * @param _image
   *          - the Image to test for collision
   * @param x1
   *          - Tọa độ x xác định của image
   * @param y1
   *          - Tọa độ y xác định của image
   * @param width1
   *          - Chiều rộng hình chữ nhật
   * @param height1
   *          - Chiều dài hình chữ nhật
   */
  public boolean checkCollidesWithRect(int x1, int y1, int width1, int height1) {
    if (!_visible) {
      return false;
    }

    if ((x1 + width1 < getRealX()) || (getRealX() + getWidth() < x1) || (y1 + height1 < getRealY()) || (getRealY() + getHeight() < y1)) {
      return false;
    }
    return true;
  }

  public boolean contains(int x1, int y1) {
    return (x1 > getRealX() && y1 > getRealY() && x1 < getRealX() + this._width && y1 < getRealY() + this._height);
  }

  public Sprite detroy() {
    removeTimerListener();
    _image = null;
    _drawListener = null;
    _timerListener = null;
    _mouseListener = null;
    _mouseMoveListener = null;
    _keyListener = null;
    _manager = null;
    return this;
  }
}
